package com.sharemedia.managers
{
	import com.sharemedia.events.PeerEvent;
	import com.sharemedia.events.TrackEvent;
	import com.sharemedia.interfaces.IAudioManager;
	import com.sharemedia.net.GroupPeer;
	import com.sharemedia.net.StreamPeer;
	import com.sharemedia.utils.ConvertUtil;
	import com.sharemedia.utils.LogUtil;
	import com.sharemedia.vos.Track;
	import com.sharemedia.vos.TrackPacket;
	import com.sharemedia.vos.TrackPrompt;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.SampleDataEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	
	import mx.collections.ArrayList;
	import mx.logging.ILogger;
	
	public class AudioPlayer extends EventDispatcher implements IAudioManager
	{
		private var logger:ILogger;
		private var musicStreamPeer:GroupPeer;
		private var feedbackPeer:StreamPeer;
		private var offerPeer:StreamPeer;
		
		public function AudioPlayer()
		{
			logger = LogUtil.getLogger(this);
			musicStreamPeer = new GroupPeer();
			feedbackPeer = new StreamPeer();
			offerPeer = new StreamPeer();
			musicStreamPeer.addEventListener(PeerEvent.PEER_CONNECTED, musicStreamConnectedHandler);
			musicStreamPeer.addEventListener(PeerEvent.PEER_MESSAGE, musicMessageHandler);
			musicStreamPeer.addEventListener("memberCountChanged", memberCountChangedHandler);
			
			sound = new Sound();
			sound.addEventListener(SampleDataEvent.SAMPLE_DATA, sampleDataReceiver);
		}
		
		private var chanel:SoundChannel;
		
		private var sound:Sound;
		
		private var _volume:Number = 1;
		
		public function get shuffle():Boolean
		{
			return false;
		}

		public function set shuffle(value:Boolean):void
		{
		}

		[Bindable]
		public function get volume():Number
		{
			return _volume;
		}
		
		public function set volume(value:Number):void
		{
			_volume = value;
			
			if(chanel)
				chanel.soundTransform = new SoundTransform(_volume);
		}
		
		private var _trackList:TrackList = new TrackList();
		
		public function get trackList():TrackList
		{
			return _trackList;
		}
		
		private var _serverName:String;
		public function get serverName():String
		{
			return _serverName;
		}
		
		[Bindable("memberCountChanged")]
		public function get memberCount():Number
		{
			return musicStreamPeer ? musicStreamPeer.memberCount : 1;
		}
			
		[Bindable("positionChanged")]
		public function get position():Number
		{
			return currentPacket ? currentPacket.position : 0;
		}
		
		public function get bufferSize():Number
		{
			return buffer ? buffer.bufferSize : 0;
		}

		public function set bufferSize(value:Number):void
		{
			if(buffer)
				buffer.bufferSize = value;
		}

		
		public function init(streamName:String):void
		{
			if(musicStreamPeer)
				musicStreamPeer.init(streamName);
			
			if(feedbackPeer)
				feedbackPeer.init(streamName + "-Feedback");
			
			if(offerPeer)
				offerPeer.init(streamName + "-Offer");
			
			_serverName = streamName;
		}
		
		private var _isPlaying:Boolean;
		public function isPlaying():Boolean
		{
			return _isPlaying;
		}
		
		public function play(track:Track=null):void
		{
			if(chanel)
				chanel.stop();
			chanel = null;
			_isPlaying = true;
			requirePlayEvent = true;
		}
		
		private var requirePlayEvent:Boolean = false;
		
		public function pause():void
		{
			if(chanel)
				chanel.stop();
			chanel = null;
			_isPlaying = false;
			
			dispatchEvent(new TrackEvent(TrackEvent.PAUSE, currentTrack));
		}
		
		public function nextTrack():void
		{
		}
		
		public function prevTrack():void
		{
		}
		
		public function like():void
		{
			if(feedbackPeer && currentPacket)
				feedbackPeer.send(currentTrack.uid, true);
		}
		
		public function unlike():void
		{
			if(feedbackPeer && currentPacket)
				feedbackPeer.send(currentTrack.uid, false);
		}
		
		public function offer(uid:String):void
		{
			if(offerPeer)
				offerPeer.send(uid, true);
		}
		
		private var _currentTrack:Track;
		
		[Bindable("currentTrackChanged")]
		public function get currentTrack():Track
		{
			return _currentTrack;
		}
		
		private var currentPacket:TrackPacket;
		
		protected function sampleDataReceiver(event:SampleDataEvent):void
		{
			currentPacket = buffer.extractPacket();
			if(!currentPacket)
			{
				if(chanel)
				{
					chanel.stop();
					chanel = null;
				}
			}
			else
			{
				event.data.writeBytes(currentPacket.bytes);
				
				if(!currentTrack || currentTrack.uid != currentPacket.trackUid)
				{
					_currentTrack = buffer.currentTrack;
					trackList.list.removeAll();
					trackList.appendTracks(buffer.getPlayedTracks());
					dispatchEvent(new Event("currentTrackChanged"));
					requirePlayEvent = true;
				}
			}
			
			if(requirePlayEvent)
			{
				dispatchEvent(new TrackEvent(TrackEvent.PLAY, currentTrack));
				requirePlayEvent = false;
			}
			
			dispatchEvent(new Event("positionChanged"));
		}
		
		
		private var _connected:Boolean;
		
		public function get connected():Boolean
		{
			return _connected;
		}
		
		
		protected function musicStreamConnectedHandler(event:Event):void
		{
			dispatchEvent(new Event("musicStreamConnected"));
			
			_connected = true;
		}
		
		[Bindable]
		public var prompt:ArrayList = new ArrayList();
		
		private var buffer:TrackBuffer = new TrackBuffer();
		
		private var latPlayedPacket:TrackPacket;
		protected function musicMessageHandler(event:PeerEvent):void
		{
			if(!_isPlaying)
				return;
			
			
			var packet:TrackPacket = ConvertUtil.toTrackPacket(event.message);
			if(!latPlayedPacket || packet.sendTime >= latPlayedPacket.sendTime)
			{
				var bufferReady:Boolean = buffer.addPacket(packet);
				
				if(bufferReady && _isPlaying)
				{
					if(!chanel)
					{
						chanel = sound.play();
						chanel.soundTransform = new SoundTransform(_volume);
						chanel.addEventListener(Event.SOUND_COMPLETE, completeHandler);
					}
				}
			}
			if(promptUID != packet.trackUid)
			{
				prompt.removeAll();
				promptUID = packet.trackUid;
			}
			
			if(prompt.length == 0 && packet.prompt)
			{
				prompt = new ArrayList(packet.prompt);
			}
			
			// update properties
			if(packet.prompt && prompt.length == 3)
			{
				updatePromps(prompt.getItemAt(0) as TrackPrompt, packet.prompt[0])
				updatePromps(prompt.getItemAt(1) as TrackPrompt, packet.prompt[1])
				updatePromps(prompt.getItemAt(2) as TrackPrompt, packet.prompt[2])
				
				var max:Number = 0;
				for each (var p:TrackPrompt in prompt.source) 
				{
					max = Math.max(max, p.votes);
				}
				
				if(max > 0)
				{
					for each ( p in prompt.source) 
					{
						p.votesPercent = p.votes/max*100;
					}
				}
			}
			
			
		}
		
		private function updatePromps(target:TrackPrompt, source:TrackPrompt):void
		{
			target.rating = source.rating;
			target.votes = source.votes;
		}
		
		private var promptUID:String;
		
		protected function completeHandler(event:Event):void
		{
			chanel = null;
		}
		
		protected function memberCountChangedHandler(event:Event):void
		{
			dispatchEvent(event);
		}
		
	}
}